namespace Asmex.FileViewer
{
    using Asmex.ObjViewer;
    using System;
    using System.IO;

    public class PEHeader : Region
    {
        private uint _AddressOfEntryPoint;
        private uint _BaseOfCode;
        private uint _BaseOfData;
        private DataDir[] _DataDirs;
        private ushort _DllFlags;
        private uint _FileAlignment;
        private uint _FileChecksum;
        private uint _HeaderSize;
        private uint _HeapCommitSize;
        private uint _HeapReserveSize;
        private uint _ImageBase;
        private uint _ImageSize;
        private uint _LoaderFlags;
        private uint _Magic;
        private byte _MajorLinkerVersion;
        private byte _MinorLinkerVersion;
        private uint _NumberOfDataDirectories;
        private ushort _OsMajor;
        private ushort _OsMinor;
        private uint _Reserved;
        private uint _SectionAlignment;
        private uint _SizeOfCode;
        private uint _SizeOfInitializedData;
        private uint _SizeOfUninitializedData;
        private uint _StackCommitSize;
        private uint _StackReserveSize;
        private ushort _SubSysMajor;
        private ushort _SubSysMinor;
        private ushort _SubSystem;
        private ushort _UserMajor;
        private ushort _UserMinor;

        public PEHeader(BinaryReader reader)
        {
            base.Start = reader.BaseStream.Position;
            this._Magic = reader.ReadUInt16();
            this._MajorLinkerVersion = reader.ReadByte();
            this._MinorLinkerVersion = reader.ReadByte();
            this._SizeOfCode = reader.ReadUInt32();
            this._SizeOfInitializedData = reader.ReadUInt32();
            this._SizeOfUninitializedData = reader.ReadUInt32();
            this._AddressOfEntryPoint = reader.ReadUInt32();
            this._BaseOfCode = reader.ReadUInt32();
            this._BaseOfData = reader.ReadUInt32();
            this._ImageBase = reader.ReadUInt32();
            this._SectionAlignment = reader.ReadUInt32();
            this._FileAlignment = reader.ReadUInt32();
            this._OsMajor = reader.ReadUInt16();
            this._OsMinor = reader.ReadUInt16();
            this._UserMajor = reader.ReadUInt16();
            this._UserMinor = reader.ReadUInt16();
            this._SubSysMajor = reader.ReadUInt16();
            this._SubSysMinor = reader.ReadUInt16();
            this._Reserved = reader.ReadUInt32();
            this._ImageSize = reader.ReadUInt32();
            this._HeaderSize = reader.ReadUInt32();
            this._FileChecksum = reader.ReadUInt32();
            this._SubSystem = reader.ReadUInt16();
            this._DllFlags = reader.ReadUInt16();
            this._StackReserveSize = reader.ReadUInt32();
            this._StackCommitSize = reader.ReadUInt32();
            this._HeapReserveSize = reader.ReadUInt32();
            this._HeapCommitSize = reader.ReadUInt32();
            this._LoaderFlags = reader.ReadUInt32();
            this._NumberOfDataDirectories = reader.ReadUInt32();
            if (this.NumberOfDataDirectories < 0x10)
            {
                throw new ModException("PEHeader:  Invalid number of data directories in file header.");
            }
            this._DataDirs = new DataDir[this.NumberOfDataDirectories];
            string[] strArray = new string[] { "Export Table", "Import Table", "Resource Table", "Exception Table", "Certificate Table", "Base Relocation Table", "Debug", "Copyright", "Global Ptr", "TLS Table", "Load Config Table", "Bound Import", "IAT", "Delay Import Descriptor", "CLI Header", "Reserved" };
            for (int i = 0; i < this.NumberOfDataDirectories; i++)
            {
                this._DataDirs[i] = new DataDir(reader, (i < 0x10) ? strArray[i] : "Unknown");
            }
            base.Length = reader.BaseStream.Position - base.Start;
        }

        [ObjViewer(Hex=true)]
        public uint AddressOfEntryPoint
        {
            get
            {
                return this._AddressOfEntryPoint;
            }
        }

        [ObjViewer(Hex=true)]
        public uint BaseOfCode
        {
            get
            {
                return this._BaseOfCode;
            }
        }

        [ObjViewer(Hex=true)]
        public uint BaseOfData
        {
            get
            {
                return this._BaseOfData;
            }
        }

        [ObjViewer(false)]
        public DataDir[] DataDirs
        {
            get
            {
                return this._DataDirs;
            }
        }

        public ushort DllFlags
        {
            get
            {
                return this._DllFlags;
            }
        }

        [ObjViewer(Hex=true)]
        public uint FileAlignment
        {
            get
            {
                return this._FileAlignment;
            }
        }

        public uint FileChecksum
        {
            get
            {
                return this._FileChecksum;
            }
        }

        public uint HeaderSize
        {
            get
            {
                return this._HeaderSize;
            }
        }

        public uint HeapCommitSize
        {
            get
            {
                return this._HeapCommitSize;
            }
        }

        public uint HeapReserveSize
        {
            get
            {
                return this._HeapReserveSize;
            }
        }

        [ObjViewer(Hex=true)]
        public uint ImageBase
        {
            get
            {
                return this._ImageBase;
            }
        }

        public uint ImageSize
        {
            get
            {
                return this._ImageSize;
            }
        }

        [ObjViewer(Hex=true)]
        public uint LoaderFlags
        {
            get
            {
                return this._LoaderFlags;
            }
        }

        [ObjViewer(Hex=true)]
        public uint Magic
        {
            get
            {
                return this._Magic;
            }
        }

        public byte MajorLinkerVersion
        {
            get
            {
                return this._MajorLinkerVersion;
            }
        }

        public byte MinorLinkerVersion
        {
            get
            {
                return this._MinorLinkerVersion;
            }
        }

        public uint NumberOfDataDirectories
        {
            get
            {
                return this._NumberOfDataDirectories;
            }
        }

        public ushort OsMajor
        {
            get
            {
                return this._OsMajor;
            }
        }

        public ushort OsMinor
        {
            get
            {
                return this._OsMinor;
            }
        }

        [ObjViewer(Hex=true)]
        public uint Reserved
        {
            get
            {
                return this._Reserved;
            }
        }

        [ObjViewer(Hex=true)]
        public uint SectionAlignment
        {
            get
            {
                return this._SectionAlignment;
            }
        }

        public uint SizeOfCode
        {
            get
            {
                return this._SizeOfCode;
            }
        }

        public uint SizeOfInitializedData
        {
            get
            {
                return this._SizeOfInitializedData;
            }
        }

        public uint SizeOfUninitializedData
        {
            get
            {
                return this._SizeOfUninitializedData;
            }
        }

        public uint StackCommitSize
        {
            get
            {
                return this._StackCommitSize;
            }
        }

        public uint StackReserveSize
        {
            get
            {
                return this._StackReserveSize;
            }
        }

        public ushort SubSysMajor
        {
            get
            {
                return this._SubSysMajor;
            }
        }

        public ushort SubSysMinor
        {
            get
            {
                return this._SubSysMinor;
            }
        }

        public ushort SubSystem
        {
            get
            {
                return this._SubSystem;
            }
        }

        public ushort UserMajor
        {
            get
            {
                return this._UserMajor;
            }
        }

        public ushort UserMinor
        {
            get
            {
                return this._UserMinor;
            }
        }
    }
}
